<template>
	<view class="con">
		<template v-if="viewWidth">
			<movable-area class="area" :style="{ height: areaHeight }">
				<movable-view v-for="(item, index) in imageList" :key="item.id" class="view" direction="all" :y="item.y"
					:x="item.x" :damping="50" :disabled="item.disable" @change="onChange($event, item)"
					@touchstart="touchstart(item)" @mousedown="touchstart(item)" @touchend="touchend(item,index)"
					@mouseup="touchend(item)" :style="{
							width: viewWidth + 'px', 
							height: viewWidth + 'px', 
							'z-index': item.zIndex, 
							opacity: item.opacity 
						  }">
					<view class="area-con" :style="{
							  width: childWidth, 
							  height: childWidth, 
							  borderRadius: props.borderRadius + 'rpx',
							  transform: 'scale(' + item.scale + ')' 
							}">
						<image class="pre-image" :src="item.src" mode="aspectFill"></image>
						<view class="del-con" @click.stop="delImages(item, index)"
							@touchstart.stop="delImages(item, index)" @touchend.stop="nothing()"
							@mousedown.stop="nothing()" @mouseup.stop="nothing()">
							<view class="del-wrap">
								<image class="del-image"
									src="">
								</image>
							</view>
						</view>
					</view>
				</movable-view>
				<view class="add" v-if="imageList.length < number"
					:style="{ top: add.y, left: add.x, width: viewWidth + 'px', height: viewWidth + 'px' }"
					@click="addImages">
					<view class="add-wrap"
						:style="{ width: childWidth, height: childWidth, borderRadius: props.borderRadius + 'rpx' }">
						<image style="width: 54rpx;height: 54rpx;"
							src="">
						</image>
					</view>
				</view>
			</movable-area>

		</template>
	</view>
</template>

<script setup>
	import { ref, computed, watch, onMounted, defineEmits, getCurrentInstance, nextTick } from 'vue'

	const props = defineProps({
		value: {
			type: Array,
			default: () => []
		},
		modelValue: {
			type: Array,
			default: () => []
		},
		keyName: {
			type: String,
			default: null
		},
		number: {
			type: Number,
			default: 9
		},
		imageWidth: {
			type: Number,
			default: 0
		},
		cols: {
			type: Number,
			default: 3
		},
		borderRadius: {
			type: Number,
			default: 0
		},
		padding: {
			type: Number,
			default: 10
		},
		scale: {
			type: Number,
			default: 1.1
		},
		opacity: {
			type: Number,
			default: 0.7
		},
		addImage: {
			type: Function,
			default: null
		},
		delImage: {
			type: Function,
			default: null
		}
	})

	const imageList = ref([])
	const width = ref(0)
	const add = ref({ x: 0, y: 0 })
	const colsValue = ref(0)
	const viewWidth = ref(0)
	const tempItem = ref(null)
	const timer = ref(null)
	const changeStatus = ref(true)
	const preStatus = ref(true)
	const first = ref(true)
	const emit = defineEmits(['input', 'update:modelValue'])
	const areaHeight = computed(() => {
		let height = ''
		if (imageList.value.length < props.number) {
			height = (Math.ceil((imageList.value.length + 1) / colsValue.value) * viewWidth.value).toFixed() + 'px'
		} else {
			height = (Math.ceil(imageList.value.length / colsValue.value) * viewWidth.value).toFixed() + 'px'
		}

		return height
	})

	const childWidth = computed(() => {
		return viewWidth.value - rpx2px(props.padding) * 2 + 'px'
	})

	watch(() => props.value, (n) => {
		if (!first.value && changeStatus.value) {
			let flag = false
			for (let i = 0; i < n.length; i++) {
				if (flag) {
					addProperties(getSrc(n[i]))
					continue
				}
				if (imageList.value.length === i || imageList.value[i].src !== getSrc(n[i])) {
					flag = true
					imageList.value.splice(i)
					addProperties(getSrc(n[i]))
				}
			}
		}
	}, { deep: true })

	watch(() => props.modelValue, (n) => {
		if (!first.value && changeStatus.value) {
			let flag = false
			for (let i = 0; i < n.length; i++) {
				if (flag) {
					addProperties(getSrc(n[i]))
					continue
				}
				if (imageList.value.length === i || imageList.value[i].src !== getSrc(n[i])) {
					flag = true
					imageList.value.splice(i)
					addProperties(getSrc(n[i]))
				}
			}
		}
	}, { deep: true })

	onMounted(() => {
		width.value = uni.getSystemInfoSync().windowWidth
		const query = uni.createSelectorQuery().in(getCurrentInstance())
		query.select('.con').boundingClientRect((data) => {
			colsValue.value = props.cols
			viewWidth.value = data.width / props.cols
			if (props.imageWidth > 0) {
				viewWidth.value = rpx2px(props.imageWidth)
				colsValue.value = Math.floor(data.width / viewWidth.value)
			}
			let list = props.value
			for (let item of list) {
				addProperties(getSrc(item))
			}
			first.value = false
		})
		query.exec()
	})

	const getSrc = (item) => {
		if (props.keyName !== null) {
			return item[props.keyName]
		}
		return item
	}


	const onChange = (e, item) => {
		if (!item) return
		let width = viewWidth.value
		item.oldX = e.detail.x
		item.oldY = e.detail.y
		if (e.detail.source === 'touch') {
			if (item.moveEnd) {
				item.offset = Math.sqrt(Math.pow(item.oldX - item.absX * width, 2) + Math.pow(item.oldY - item
					.absY * width, 2))
			}
			let x = Math.floor((e.detail.x + width / 2) / width)
			if (x >= colsValue.value) return
			let y = Math.floor((e.detail.y + width / 2) / width)
			let index = colsValue.value * y + x
			if (item.index != index && index < imageList.value.length) {
				changeStatus.value = false
				for (let obj of imageList.value) {
					if (item.index > index && obj.index >= index && obj.index < item.index) {
						change(obj, 1)
					} else if (item.index < index && obj.index <= index && obj.index > item.index) {
						change(obj, -1)
					} else if (obj.id != item.id) {
						obj.offset = 0
						obj.x = obj.oldX
						obj.y = obj.oldY
						setTimeout(() => {
							nextTick(() => {
								obj.x = obj.absX * width
								obj.y = obj.absY * width
							})
						}, 0)
					}
				}
				item.index = index
				item.absX = x
				item.absY = y
				if (!item.moveEnd) {
					setTimeout(() => {
						nextTick(() => {
							item.x = item.absX * width
							item.y = item.absY * width
						})
					}, 0)
				}
				sortList()
			}
		}
	}

	const change = (obj, i) => {
		obj.index += i
		obj.offset = 0
		obj.x = obj.oldX
		obj.y = obj.oldY
		obj.absX = obj.index % colsValue.value
		obj.absY = Math.floor(obj.index / colsValue.value)
		setTimeout(() => {
			nextTick(() => {
				obj.x = obj.absX * viewWidth.value
				obj.y = obj.absY * viewWidth.value
			})
		}, 0)
	}

	const touchstart = (item) => {
		imageList.value.forEach(v => {
			v.zIndex = v.index + 9
		})
		item.zIndex = 99
		item.moveEnd = true
		tempItem.value = item
		timer.value = setTimeout(() => {
			item.scale = props.scale
			item.opacity = props.opacity
			clearTimeout(timer.value)
			timer.value = null
		}, 200)
	}

	const touchend = (item) => {
		previewImage(item)
		item.scale = 1
		item.opacity = 1
		item.x = item.oldX
		item.y = item.oldY
		item.offset = 0
		item.moveEnd = false
		setTimeout(() => {
			nextTick(() => {
				item.x = item.absX * viewWidth.value
				item.y = item.absY * viewWidth.value
				tempItem.value = null
				changeStatus.value = true
			})
		}, 0)
	}


	const previewImage = (item) => {
		if (timer.value&& preStatus.value && changeStatus.value && item.offset < 28.28) {
			const list = imageList.value
			let srcList = list.map(v => v.src)
			uni.previewImage({
				urls: srcList,
				current: item.index,
				success: () => {
					// preStatus.value = false
					// setTimeout(() => {
					// 	preStatus.value = true
					// }, 200)
				},
				fail: (e) => {
					console.log(e);
				}
			})
		}

		if (timer.value) {
			clearTimeout(timer.value)
			timer.value = null
		}
	}


	const mouseenter = () => {
		if (timer.value) {
			clearTimeout(timer.value)
			timer = null
		}
	}

	const mouseleave = () => {
		if (timer.value) {
			clearTimeout(timer.value)
			timer = null
		}
	}

	const addProperties = (src) => {
		let absX = imageList.value.length % colsValue.value
		let absY = Math.floor(imageList.value.length / colsValue.value)
		let x = absX * viewWidth.value
		let y = absY * viewWidth.value
		imageList.value.push({
			src: src,
			x,
			y,
			oldX: x,
			oldY: y,
			absX,
			absY,
			scale: 1,
			zIndex: 9,
			opacity: 1,
			index: imageList.value.length,
			id: Math.random().toString(36).substr(2, 10),
			disable: false,
			offset: 0,
			moveEnd: false
		})
		add.value.x = (imageList.value.length % colsValue.value) * viewWidth.value + 'px'
		add.value.y = Math.floor(imageList.value.length / colsValue.value) * viewWidth.value + 'px'

	}

	const delImages = (item, index) => {
		imageList.value.splice(index, 1)
		for (let obj of imageList.value) {
			if (obj.index > item.index) {
				obj.index -= 1
				obj.x = obj.oldX
				obj.y = obj.oldY
				obj.absX = obj.index % colsValue.value
				obj.absY = Math.floor(obj.index / colsValue.value)
				nextTick(() => {
					obj.x = obj.absX * viewWidth.value
					obj.y = obj.absY * viewWidth.value
				})
			}
		}
		add.value.x = (imageList.value.length % colsValue.value) * viewWidth.value + 'px'
		add.value.y = Math.floor(imageList.value.length / colsValue.value) * viewWidth.value + 'px'
		sortList()
	}




	const nothing = () => {}

	const rpx2px = (rpx) => {
		return (width.value / 750) * rpx
	}

	const addImages = () => {
		if (typeof props.addImage === 'function') {
			addImage.bind(this.$parent)()
		} else {
			let checkNumber = props.number - imageList.value.length
			uni.chooseImage({
				count: checkNumber,
				sourceType: ['album', 'camera'],
				success: res => {
					let count = checkNumber <= res.tempFilePaths.length ? checkNumber : res.tempFilePaths
						.length
					for (let i = 0; i < count; i++) {
						addProperties(getSrc(res.tempFilePaths[i]))
					}
					sortList()
				}
			})
		}
	}


	const sortList = () => {
		const result = []
		let source = props.modelValue
		let list = imageList.value.slice()
		list.sort((a, b) => {
			return a.index - b.index
		})
		for (let s of list) {
			let item = source.find(d => getSrc(d) == s.src)
			if (item) {
				result.push(item)
			} else {
				if (props.keyName !== null) {
					result.push({
						[props.keyName]: s.src
					})
				} else {
					result.push(s.src)
				}
			}
		}
		emit("input", result);
		emit("update:modelValue", result);
	}
</script>

<style scoped lang="scss">
	.con {
		.area {
			width: 100%;

			.view {
				display: flex;
				justify-content: center;
				align-items: center;

				.add-wrap {
					display: flex;
					justify-content: center;
					align-items: center;
					background-color: #eeeeee;
				}

				.area-con {
					position: relative;
					overflow: hidden;

					.pre-image {
						width: 100%;
						height: 100%;
					}

					.del-con {
						position: absolute;
						top: 10rpx;
						right: 10rpx;
						padding: 0 0 20rpx 20rpx;

						.del-wrap {
							width: 36rpx;
							height: 36rpx;
							// background-color: rgba(0, 0, 0, 0.4);
							background-color: #3D5CFF;
							border-radius: 100rpx;
							display: flex;
							justify-content: center;
							align-items: center;

							.del-image {
								width: 14rpx;
								height: 14rpx;
							}
						}
					}
				}
			}

			.add {
				display: flex;
				justify-content: center;
				align-items: center;
				position: absolute;
				overflow: hidden;

				.add-wrap {
					overflow: hidden;

					display: flex;
					justify-content: center;
					align-items: center;
					background-color: #eeeeee;
				}
			}
		}
	}
</style>